package com.chen.androidstudy.utils

import android.media.MediaCodec
import android.media.MediaExtractor
import android.media.MediaFormat
import android.os.AsyncTask
import android.util.Log
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL

/**
 *
 * @Author:        CHong
 * @CreateDate:    2024/12/10 0010
 * @Description:
 */


class DownloadAndConvertTask constructor(cacheDirPath: String) : AsyncTask<String?, Void?, Void?>() {
    private var TAG = "DownloadAndConvertTask"

    //缓存路径
    var cacheDir = File(cacheDirPath)
    override fun doInBackground(vararg params: String?): Void? {
        if (params.isNotEmpty()) {
            val audioUrl = params[0]
            if (audioUrl != null) {
//                val path = downloadAudioFile(audioUrl)
                val pcmPath = decodeAudioToPcm(audioUrl)
                if (!pcmPath.isNullOrBlank()) {
                    // TODO ... 这里拿到pcm文件路径

                }
            }
        }
        return null
    }

    /**
     * 下載音频文件到本地
     * @param audioUrl 要下载的音频链接
     * @return 下载到本地的文件路径
     */
    private fun downloadAudioFile(audioUrl: String): String {
        Log.d("downloadAndConvert", "audioUrl = " + audioUrl)
        var urlConnection: HttpURLConnection?
        var outputStream: FileOutputStream?

        // Open a connection to the audio file URL
        val url = URL(audioUrl)
        urlConnection = url.openConnection() as HttpURLConnection
        urlConnection.connect()

        // Create a temporary file to store the downloaded audio data
        val tempFile: File = File.createTempFile("temp_audio", null, cacheDir)

        outputStream = FileOutputStream(tempFile)

        // Write the downloaded audio data to the temporary file
        val buffer = ByteArray(1024)
        var bytesRead: Int
        while (urlConnection.inputStream.read(buffer).also { bytesRead = it } != -1) {
            outputStream.write(buffer, 0, bytesRead)
        }
        outputStream.close()
        urlConnection.inputStream.close()
        urlConnection.disconnect()

        return tempFile.absolutePath

    }

    /**
     * 音頻文件解码为pcm格式
     * @param inputAudioFile 要转码的音频文件路径
     * @return pcm文件路径
     */
    private fun decodeAudioToPcm(inputAudioFile: String): String? {
        Log.d(TAG, "decodeAudioToPcm start inputAudioFile = $inputAudioFile")

        if (inputAudioFile.isEmpty()) {
            return null
        }
        if (inputAudioFile.endsWith(".pcm")) {
            return inputAudioFile
        }
        val timeStart = System.currentTimeMillis()
        var file = File(inputAudioFile)

        val mediaExtractor = MediaExtractor()
        var mediaCodec: MediaCodec? = null
        var outputStream: FileOutputStream? = null
        var startTime = System.currentTimeMillis()
        if (file.name.endsWith(".flac")){
            return ""
        }
        val tempPcmFile: File = File.createTempFile("temp_audio_pcm", ".pcm", cacheDir)
        try {
            // 设置数据源为输入音频文件
            mediaExtractor.setDataSource(inputAudioFile)

            // 选择第一个默认轨道（多个音轨时，请注意选择音轨）
            mediaExtractor.selectTrack(0)

            // 获取音频轨道的格式
            val inputFormat = mediaExtractor.getTrackFormat(0)

            Log.d(TAG, "decodeAudioToPcm MediaFormat.KEY_MIME = " + inputFormat.getString(MediaFormat.KEY_MIME))

            // 创建用于解码的 MediaCodec
            mediaCodec = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME)!!)
            mediaCodec.configure(inputFormat, null, null, 0)
            mediaCodec.start()

            // 开始解码并写入到 PCM 文件
            val codecInputBuffers = mediaCodec.inputBuffers
            val codecOutputBuffers = mediaCodec.outputBuffers
            val bufferInfo = MediaCodec.BufferInfo()

            // 输出 PCM 文件的路径
            val pcmOutputFile = File(tempPcmFile.absolutePath)
            outputStream = FileOutputStream(pcmOutputFile)

            var isDone = false
            while (!isDone) {
                // 获取可用的输入缓冲区
                val inputBufferIndex = mediaCodec.dequeueInputBuffer(10000)
                if (inputBufferIndex >= 0) {
                    // 获取输入缓冲区
                    val inputBuffer = codecInputBuffers[inputBufferIndex]
                    // 读取音频数据到输入缓冲区
                    val sampleSize = mediaExtractor.readSampleData(inputBuffer, 0)
                    if (sampleSize < 0) {
                        // 如果没有更多数据，发送 EOS（End of Stream）标志
                        mediaCodec.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)
                        isDone = true
                    } else {
                        // 将数据放入输入缓冲区，并前进到下一帧
                        mediaCodec.queueInputBuffer(inputBufferIndex, 0, sampleSize, mediaExtractor.sampleTime, 0)
                        mediaExtractor.advance()
                    }
                }

                // 获取解码后的输出数据
                val outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 10000)
                if (outputBufferIndex >= 0) {
                    // 获取输出缓冲区
                    val outputBuffer = codecOutputBuffers[outputBufferIndex]
                    // 将 PCM 数据从输出缓冲区中读取到字节数组中
                    val chunk = ByteArray(bufferInfo.size)
                    outputBuffer.get(chunk)
                    outputBuffer.clear()

                    // 将 PCM 数据写入到输出文件
                    outputStream.write(chunk, 0, chunk.size)

                    // 释放输出缓冲区
                    mediaCodec.releaseOutputBuffer(outputBufferIndex, false)
                }
            }
            Log.e(TAG, "endTime:" +  (System.currentTimeMillis() - startTime));
        } catch (e: IOException) {
            Log.e(TAG, "Error decoding audio to PCM: " + e.message)
            return null
        } finally {
            try {
                // 关闭资源
                if (mediaCodec != null) {
                    mediaCodec.stop()
                    mediaCodec.release()
                }
                mediaExtractor.release()
                outputStream?.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }

        Log.d(TAG, "decodeAudioToPcm end outputPcmFile = " + tempPcmFile.absolutePath)
        Log.d(TAG, "decodeAudioToPcm end outputPcmFile.length = " + tempPcmFile.length())
        Log.d(TAG, "decodeAudioToPcm time consuming (ms) = " + (System.currentTimeMillis() - timeStart))

        return tempPcmFile.absolutePath
    }
}
